package yxy.banana.socket.controller.client;

import yxy.apple.protobuf.Constants;
import yxy.apple.protobuf.Message.Parcal;
import yxy.apple.protobuf.external.gateway.Gateway.AuthenticationRequest;
import yxy.apple.protobuf.external.gateway.Gateway.AuthenticationResponse;
import yxy.apple.util.IDGenerator;
import yxy.banana.socket.NetworkElement;
import yxy.banana.socket.controlled.ControlledHandler;
import yxy.banana.socket.receiver.Observer;
import yxy.banana.socket.receiver.Receivable;

public abstract class ClientAuthenticationController<H extends ControlledHandler> extends ClientHeartbeatController<H> {

	private boolean authenticated = false;
	private boolean authenticating = false;

	public boolean authenticated() {
		return this.authenticated;
	}

	public boolean authenticating() {
		return this.authenticating;
	}

	/**
	 * 发起注册，一般情况下应在actived回调后进行
	 * 
	 * @param exclusion
	 * @throws Exception
	 */
	public void authenticate(String ethic, String pcode, String token, int timeout) throws Exception {
		if (this.authenticating || this.authenticated) {
			new RuntimeException("It is authenticating or authenticated already!");
		}
		this.authenticating = true;
		AuthenticationRequest.Builder builder = AuthenticationRequest.newBuilder();
		builder.setEthic(ethic);
		builder.setToken(token);
		AuthenticationRequest request = builder.build();
		Receivable receiver = new Receivable() {
			@Override
			public void receive(Parcal parcal) {
				boolean result = false;
				if (parcal.getProto() == Constants.ControlProto.AuthenticationResponse) {
					try {
						AuthenticationResponse response = AuthenticationResponse.parseFrom(parcal.getData());
						result = response.getResult();
					} catch (Exception e) {
						ClientAuthenticationController.this.caught(e);
					}
				} else {
					ClientAuthenticationController.this.drop(parcal, DroppedReason.UnknownProto);
				}
				ClientAuthenticationController.this.callback(result);
			}

			@Override
			public void exipred(String tag) {
				ClientAuthenticationController.this.callback(false);
			}
		};
		try {
			String tag = IDGenerator.nextUUID();
			Observer.shared.observe(tag, timeout, receiver);
			long s = NetworkElement.value(this.handler().system(), this.handler().cluster(), this.handler().channel());
			long d = 0;
			this.handler().send(tag, s, d, Constants.ControlProto.AuthenticationRequest, request);
		} catch (Exception e) {
			this.authenticating = false;
			throw e;
		}
	}

	private void callback(boolean result) {
		this.authenticated = result;
		this.authenticating = false;
		this.authenticated(result);
	}

	/**
	 * 认证请求得到回应后回调，如果认证失败，应认证关闭连接（也有可能被服务器强制断开）
	 * 
	 * @param result
	 */
	protected abstract void authenticated(boolean result);

}
